import {DecoratorMetadata} from '../decorator/meta/DecoratorMetadata';
import {IMetadata} from '../decorator/meta';
import {DecoratorType} from '../decorator/meta/DecoratorType';
import {ObjectType} from '../type';


/**
 * 反射工具类
 * @author Zhang
 * @date 2019/3/18 14:17
 */
export class ReflectUtils {

  /**
   * 判断给定参数是不是一个class
   * @param t
   */
  static isClass(t): boolean {
    return !!t && t.toString().startsWith('class ') && typeof t === 'function';
  }

  /**
   * 判断给定参数是不是类的成员方法
   * @param m
   */
  static isMethod(m): boolean {
    return !!m && typeof m === 'function' && !ReflectUtils.isClass(m);
  }

  /**
   * 获取对象命名空间, 需要在IOC容器初始化之后才能获取到
   * @param target
   */
  static getNamespace(target: Object): string {
    return Reflect.getMetadata('summer:namespace', target);
  }

  /**
   * 获取某对象上的反射元数据
   * @param key metadata key
   * @param target target object
   * @param propertyKey 指定属性名
   */
  static getMetadata(key: DecoratorType, target: Object, propertyKey?: string | symbol): Array<DecoratorMetadata | IMetadata> {
    return Reflect.getMetadata(key, target, propertyKey);
  }

  /**
   * 向对象上添加装饰器元数据
   * @param key metadata key
   * @param metadata metadata
   * @param target target object
   * @param propertyKey 指定属性名,添加到对象的指定属性上
   */
  static addMetadata(key: DecoratorType, metadata: DecoratorMetadata, target: Object, propertyKey?: string | symbol): void {
    const metadatas: Array<DecoratorMetadata> = Reflect.getMetadata(key, target, propertyKey) || [];
    metadatas.push(metadata);
    Reflect.defineMetadata(key, metadatas, target, propertyKey);
  }

  /**
   * 向被修饰对象添加装饰器信息
   * @param metadata 装饰器信息
   * @param target 被修饰对象
   */
  static addClassDecorator(metadata: DecoratorMetadata, target: ObjectType<any>): void {
    this.addMetadata('summer:class:decorator', metadata, target);
  }

  /**
   * 向装饰器对象上添加被修饰对象信息
   * @param metadata 被修饰对象信息
   * @param targetDecorator 装饰器对象
   */
  static addDecoratorTarget(metadata: IMetadata, targetDecorator: Function): void {
    const metadatas: Array<IMetadata> = Reflect.getMetadata('summer:decorator:target', targetDecorator) || [];
    metadatas.push(metadata);
    Reflect.defineMetadata('summer:decorator:target', metadatas, targetDecorator);
  }

  /**
   * 获取被某装饰器修饰的所有对象信息
   * @param targetDecorator 目标装饰器
   */
  static getTargets(targetDecorator: Function): IMetadata[] {
    return Reflect.getMetadata('summer:decorator:target', targetDecorator) || [];
  }

  /**
   * 判断某对象有没有被某装饰器修饰
   * @param key metadata array key
   * @param target 被修饰对象
   * @param targetDecorator 装饰器对象
   * @param propertyKey 指定对象的属性名
   */
  static hasDecorator(key: DecoratorType, target: Object, targetDecorator: Function, propertyKey?: string | symbol): boolean {
    const metadatas: DecoratorMetadata[] = Reflect.getMetadata(key, target, propertyKey);
    return !!metadatas && metadatas.findIndex(item => item.decorator === targetDecorator) !== -1;
  }


  /**
   * 获取修饰某对象的某装饰器
   * @param key metadata key
   * @param target 目标对象
   * @param targetDecorator 目标装饰器
   * @param propertyKey 指定对象的属性名
   */
  static getDecorator(key: DecoratorType, target: Object, targetDecorator: Function, propertyKey?: string | symbol): DecoratorMetadata {
    const metadatas: DecoratorMetadata[] = Reflect.getMetadata(key, target);
    return !!metadatas && metadatas.find(item => item.decorator === targetDecorator);
  }

  /**
   * 获取修饰某对象的全部装饰器
   * @param key
   * @param target
   */
  static getDecorators(key: DecoratorType, target: Object): DecoratorMetadata[] {
    return Reflect.getMetadata(key, target) || [];
  }

  /**
   * 获取修饰某类的某装饰器
   * @param target 类对象
   * @param targetDecorator 目标装饰器
   */
  static getClassDecorator(target: Object, targetDecorator: Function): DecoratorMetadata {
    return ReflectUtils.getDecorator('summer:class:decorator', target, targetDecorator);
  }

  /**
   * 获取修饰某类的全部装饰器
   * @param target 类对象
   */
  static getClassDecorators(target: Object): DecoratorMetadata[] {
    return ReflectUtils.getDecorators('summer:class:decorator', target);
  }

  static getPropertyDecorators() {

  }
}
